;; general command definitions

(defun delete-current-file ()
  "Delete the file associated with the current buffer. Delete the current buffer too."
  (interactive)
  (let ((currentFile (buffer-file-name)))
    (when (yes-or-no-p (concat "Delete file: " currentFile))
      (kill-buffer (current-buffer))
      (delete-file currentFile)
      (message (concat "Deleted file: " currentFile)))))

(defun make-backup ()
  "Make a backup copy of current buffer's file.
The new file's name is the old file name with '~' added at the end, in the same dir. If such file exists, append more '~'. If the current buffer is not associated with a file, it's an error."
  (interactive)
  (let ((currentFileName (buffer-file-name))
          (toFileName (concat currentFileName "~")))
    (while (file-exists-p toFileName)
      (setq toFileName (concat toFileName "~")))
    (copy-file currentFileName toFileName t)
    (message (concat "Backup saved as: " (file-name-nondirectory toFileName)))))


(defun unit-at-cursor (unit)
  "Return the string and boundary of UNIT under cursor.
Return a vector [text a b], where text is the string and a, b are its boundary.

UNIT can be:
* 'word -- sequence of 0 to 9, A to Z, a to z, and hyphen.
* 'glyphs -- sequence of visible glyphs. Useful for file name, URL, etc. which don't have spaces in them.
* 'line -- delimited by \\n
* 'block -- delimited by \\n\\n or beginning and end of buffer
* 'buffer -- whole buffer
* a vector [beginRegex endRegex] -- the elements are regex strings used to determine the beginning and end of boundary chars. They are passed to 'skip-chars-backward' and 'skip-chars-forward'.

This function is similar to 'thing-at-point' and 'bounds-of-thing-at-point'.
The main differences are:
* this function returns the text and the 2 boundaries as a vector at one shot.
* 'line always returns the line without end of line character, avoiding inconsistency when the line is at end of buffer.
* 'block does not depend on syntax table. (TODO: ??)"
  (let (begin end)
    (save-excursion
      (cond
       ((eq unit 'word)
	(let ((wordcharset "-A-Za-z"))
	  (skip-chars-backward wordcharset)
	  (setq begin (point))
	  (skip-chars-forward wordcharset)
	  (setq end (point))))
       ((eq unit 'glyphs)
	(progn
	  (skip-chars-backward "[:graph:]")
	  (setq begin (point))
	  (skip-chars-forward "[:graph:]")
	  (setq end (point))))
       ((eq unit 'line)
	(progn
	  (setq begin (line-beginning-position))
	  (setq end (line-end-position))))
       ((eq unit 'buffer)
	(progn
	  (setq begin (point-min))
	  (setq end (point-max))))
       ((eq unit 'block)
	(progn
	  (if (re-search-backward "\n\n" nil t)
	      (progn
		(forward-char 2)
		(setq begin (point)))
	    (setq begin (point-min)))
	  (if (re-search-forward "\n\n" nil t)
	      (progn
		(backward-char)
		(setq end (point)))
	    (setq end (point-max)))))
       ((vectorp unit)
	(let ((current (point)))
	  (skip-chars-backward (elt unit 0))
	  (setq begin (point))
	  (goto-char current)
	  (skip-chars-forward (elt unit 1))
	  (setq end (point))))))
    (vector (buffer-substring-no-properties begin end) begin end)))


(defun get-selection-or-unit (unit)
  "Return the string and boundary of text selection or UNIT under cursor.

If 'region-active-p' is true, then the region is the unit. Else, it depends on the UNIT. See 'unit-at-cursor'.
Returns a vector [text a b], where text is the string and a, b are its boundaries."
;;  (interactive)
  (let (begin end)
    (if (region-active-p)
	(progn
	  (setq begin (region-beginning))
	  (setq end (region-end))
	  (vector (buffer-substring-no-properties begin end) begin end))
      (unit-at-cursor unit))))


(defun count-words-region-or-line ()
  "Print number of words and chars in text selection or line."
  (interactive)
  (message "Counting ...")
  (let ((tuples (get-selection-or-unit 'line)))
    (setq begin (elt tuples 1))
    (setq end (elt tuples 2))
    (save-excursion
      (let ((wordCount 0)
	 (charCount (- end begin)))
	(goto-char begin)
	(while (and
		(< (point) end)
		(re-search-forward "\\w+\\W*" end t))
	  (setq wordCount (1+ wordCount)))
	(message "Words: %d. Chars: %d." wordCount charCount)))))
